#include "Technique.h"

static const char* pVSName = "VS";
static const char* pFSName = "FS";
const char* ShaderType2ShaderName(GLuint Type)
{
    switch (Type) 
	{
        case GL_VERTEX_SHADER:
            return pVSName;
        case GL_FRAGMENT_SHADER:
            return pFSName;
    }

    return NULL;
}


Technique::Technique(void)
{
	m_shaderProg = 0;
}


Technique::~Technique(void)
{
	// Delete the intermediate shader objects that have been added to the program
    // The list will only contain something if shaders were compiled but the object itself
    // was destroyed prior to linking.
    for (ShaderObjList::iterator it = m_shaderObjList.begin() ; it != m_shaderObjList.end() ; it++)
    {
        glDeleteShader(*it);
    }

    if (m_shaderProg != 0)
    {
        glDeleteProgram(m_shaderProg);
        m_shaderProg = 0;
    }
}


bool Technique::Init()
{
	 m_shaderProg = glCreateProgram();

    if (m_shaderProg == 0) {
        fprintf(stderr, "Failed to creat shader program\n");
        return false;
    }

    return true;

	/*
	m_shaderProg = LoadShaders();
	if(m_shaderProg == 0)
	{
		std::cout <<"Coudn't create the shader" << std::endl;
		return false;
	}

	return true;*/
}

bool Technique::AddShader(GLenum ShaderType, const char * file_path)
{
	// Create the shaders
	GLuint ShaderObj = glCreateShader(ShaderType);
	if (ShaderObj == 0) 
	{
        fprintf(stderr, "Error creating shader type %d\n", ShaderType);
		
		std::cout << "glCreateShader failed" << std::endl;
		if (std::cin.get() == '\n')		
			return false;
    }

	// Save the shader object - will be deleted in the destructor
    m_shaderObjList.push_back(ShaderObj);

	// Read the Vertex Shader code from the file
	std::string ShaderCode;
	std::ifstream ShaderStream(file_path, std::ios::in);
	if(ShaderStream.is_open())
	{
		std::string Line = "";
		while(getline(ShaderStream, Line))
			ShaderCode += "\n" + Line;
		ShaderStream.close();
	}
	else
	{
		printf("Impossible to open %s. Right directory? \n", file_path);

		
		if (std::cin.get() == '\n')		
			return false;
	}


	// Compile Shader
	printf("Compiling shader : %s\n", file_path);
	char const * SourcePointer = ShaderCode.c_str();
	glShaderSource(ShaderObj, 1, &SourcePointer , NULL);
	glCompileShader(ShaderObj);


	GLint Result = GL_FALSE;
	int InfoLogLength;


	// Check Shader
	glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &Result);
	glGetShaderiv(ShaderObj, GL_INFO_LOG_LENGTH, &InfoLogLength);
	
	if ( InfoLogLength > 0 )
	{
		std::vector<char> VertexShaderErrorMessage(InfoLogLength+1);
		glGetShaderInfoLog(ShaderObj, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
		printf("%s\n", &VertexShaderErrorMessage[0]);
	}

	glAttachShader(m_shaderProg, ShaderObj);

	return true;
}

bool Technique::Finalize()
{
	GLint success = 0;
	int InfoLogLength;
	
	// Link the program
	glLinkProgram(m_shaderProg);

	glGetProgramiv(m_shaderProg, GL_LINK_STATUS, &success);
	glGetProgramiv(m_shaderProg, GL_INFO_LOG_LENGTH, &InfoLogLength);
	if ( InfoLogLength > 0 )
	{
		std::vector<char> ProgramErrorMessage(InfoLogLength+1);
		glGetProgramInfoLog(m_shaderProg, InfoLogLength, NULL, &ProgramErrorMessage[0]);
		printf("%s\n", &ProgramErrorMessage[0]);
	}

	for(ShaderObjList::iterator it = m_shaderObjList.begin(); it != m_shaderObjList.end(); it++)
	{
		 glDeleteShader(*it);
	}

	m_shaderObjList.clear();

	return true;
}

void Technique::Enable()
{
	glUseProgram(m_shaderProg);
}


GLuint Technique::GetUniformLocation(const char* varName)
{
    GLuint Location = glGetUniformLocation(m_shaderProg, varName);

    if (Location == (GLuint)0xFFFFFFFF) 
	{
        fprintf(stderr, "Warning! Unable to get the location of uniform '%s'\n", varName);
		std::cin.get();
    }

    return Location;
}